import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PGraphics;
import controlP5.Button;
import controlP5.ControlEvent;
import controlP5.ControlFont;
import controlP5.ControlP5;
import controlP5.ControlTimer;
import controlP5.Controller;
import controlP5.ListBox;
import controlP5.Slider;
import controlP5.Textlabel;    
    
    
public class AppRunner extends PApplet {

    private static final long serialVersionUID = 1L;
    private static final String PAUSED = "PAUSED";
    private static final String MAP_LOADING = "MAP LOADING...";
    private static final String IMAGE_SAVING = "IMAGE SAVING...";
    private static final String EAST_AREA_DATA_FILE = "rotterdamopendata_hoogtebestandtotaal_oost.csv";
    private static final String WEST_AREA_DATA_FILE = "rotterdamopendata_hoogtebestandtotaal_west.csv";
    private static final String EMPTY = "";
    private static String PATH_DATA = "";
    private float xTrans = 75;
    private float yTrans = 70;
    private float zTrans = -1;
    private int color1;
    private int backgroundColor = 0;
    private String[] tokens = new String[1001];
    private boolean needDraw = Boolean.FALSE;
    private CityMap cityMap;

    private ControlP5 cp5;
    private ControlTimer timer;
    private Textlabel lblTimer;
    private Textlabel lblSliderUnit;
    private Textlabel lblMapSize;
    private Textlabel lblStatus;
    private Textlabel lblMapLoading;
    private Textlabel lblImageSaving;
    private Textlabel lblNorth;
    private Textlabel lblWest;
    private Textlabel lblEast;
    private Textlabel lblSouth;
    private Slider sldTimeSpeed;
    private Button btnPlay;
    private Button btnPause;
    private Button btnResetTimer;
    private Button btnCaptureScreen;
    private Button btnCaptureScreen6Situations;
    private Button btnLoadWestArea;
    private Button btnLoadEastArea;
    private ListBox lbxMapSize;
    private PGraphics pg;
    

    public void setup() {
        PATH_DATA = dataPath(""); 
//        PATH_DATA = "C:\\Users\\Thien\\Desktop\\test\\";
        size(800, 600);
        
        background(backgroundColor);
        float width = 500f;
        float height = 500f;
        
        cp5 = new ControlP5(
                this);

        sldTimeSpeed = cp5.addSlider(
                "Time Speed").setPosition(
                20, 40).setWidth(
                15).setHeight(
                500).setRange(
                1, 3600) // values can range from big to small as well
                .setValue(
                        1).setNumberOfTickMarks(
                                3600).setSliderMode(
                        Slider.FLEXIBLE);
        lblSliderUnit = new Textlabel(
                cp5, "lblSliderUnit", 5, 18);
        lblSliderUnit.setValue("interval (seconds)");

        btnPlay = cp5.addButton(
                "play").setValue(
                128).setPosition(
                140, 13).updateSize();
        color1 = btnPlay.getColor().getBackground();
        btnPause = cp5.addButton(
                "pause").setValue(
                128).setPosition(
                210, 13).updateSize();
        btnResetTimer = cp5.addButton(
                "reset timer").setValue(
                128).setPosition(
                280, 13).updateSize();
        setLock(btnPlay, true);
        
        timer = new ControlTimer();
        lblTimer = new Textlabel(
                cp5, "--", 570, 15);
        timer.setSpeedOfTime(sldTimeSpeed.getValue());

        lblStatus = new Textlabel(
                cp5, "lblStatus", this.width - 160, 40);
        PFont pfont = createFont("Arial",20,true); // use true/false for smooth/no-smooth
        ControlFont font = new ControlFont(pfont,30);
        lblStatus.setValue(EMPTY).setFont(font);
        
        
        lbxMapSize = cp5.addListBox(
                "lbxMapSize").setPosition(
                435, 30).setItemHeight(
                30).setBarHeight(
                15).setColorBackground(
                color1).setColorActive(
                color(0)).setColorForeground(
                color(255, 100, 0)).close();
        lbxMapSize.setCaptionLabel("500 x 500");
        lbxMapSize.getCaptionLabel().style().marginTop = 3;
        lbxMapSize.getCaptionLabel().style().marginTop = 3;
        
        tokens[500] = "500 x 500";
        lbxMapSize.addItem(tokens[500], 500);
        tokens[1000] = "1000 x 1000";
        lbxMapSize.addItem(tokens[1000], 1000);
        
        lblMapSize = new Textlabel(
                cp5, "lblMapSize", 380, 18);
        lblMapSize.setValue("MAP SIZE");
        
        
        btnCaptureScreen = cp5.addButton(
                "capture screen").setValue(
                128).setPosition(
                100, 40).setWidth(90).updateSize();
        btnCaptureScreen6Situations = cp5.addButton(
                "capture with 6 situations").setValue(
                256).setPosition(
                200, 40).setWidth(140).updateSize();
        
        
        
        btnLoadWestArea = cp5.addButton(
                "load west area").setValue(
                128).setPosition(
                400, 40).setWidth(90).updateSize();
        btnLoadEastArea = cp5.addButton(
                "load east area").setValue(
                256).setPosition(
                500, 40).setWidth(140).updateSize();
        setLock(btnLoadWestArea, true);
        
        
        lblNorth = new Textlabel(
                cp5, "NORTH", this.width - 130, 150);
        lblNorth.setValue("NORTH");
        
        lblSouth = new Textlabel(
                cp5, "SOUTH", this.width - 130, 220);
        lblSouth.setValue("SOUTH");
        
        lblWest = new Textlabel(
                cp5, "WEST", this.width - 175, 185);
        lblWest.setValue("WEST");
        
        lblEast = new Textlabel(
                cp5, "EAST", this.width - 80, 185);
        lblEast.setValue("EAST");
        
        lblMapLoading = new Textlabel(
                cp5, "Map loading", this.width - 175, 500);
        pfont = createFont("Arial",15,true); // use true/false for smooth/no-smooth
        font = new ControlFont(pfont,15);
        lblMapLoading.setValue(EMPTY).setFont(font);
        
        lblImageSaving = new Textlabel(
                cp5, "lblImageSaving", this.width - 175, 300);
        pfont = createFont("Arial",15,true); // use true/false for smooth/no-smooth
        font = new ControlFont(pfont,15);
        lblImageSaving.setValue(EMPTY).setFont(font);
        
//        cityMap = new CityMap(
//                new Point3d(68585.7894897f + (width / 2), 447031.057678f + (height / 2), 0.0f), width, height); 
        cityMap = new CityMap(
                new Point3d(0.0f, 0.0f, 0.0f), width, height); 
        
        cityMap.setCsvPath(PATH_DATA + "/" + WEST_AREA_DATA_FILE);
        loadCityMap();
    }

    private String previusTimerString = "";
    private int previusHour = 0;
    
    public void draw() {
        background(backgroundColor);
        String timeString = timer.toString();
        timeString = String.format("%02d : %02d : %02d", 
                (timer.day() * 24) + timer.hour(), 
                timer.minute(), timer.second());
        if (timer.day() == 0 && timer.hour() == 0 
                && timer.minute() == 0 && timer.second() == 0) {
            timeString = previusTimerString;
        }
        previusTimerString = timeString;
        lblTimer.setValue("CURRENT TIME  " + timeString);
        lblTimer.draw(this);
        
        if (cityMap != null) {
            int hour = timer.hour();
            if (timer.day() > 0) {
                hour = timer.hour() + (timer.day() * 24);
            } else if (timer.millis() == 0) {
                hour = previusHour;
            }
            previusHour = hour;
            cityMap.setWaterHeight(hour * 0.5f);
        }
        lblSliderUnit.draw(this);
        lblMapSize.draw(this);
        lblStatus.draw(this);
        if (lblMapLoading != null) {
            lblMapLoading.draw(this);
        }
        if (lblImageSaving != null) {
            lblImageSaving.draw(this);
        }
        if (cityMap != null) {
            cityMap.display(this, xTrans, yTrans);
        }
        fill(255);
        if (lblEast != null) {
            lblEast.draw(this);
        }
        if (lblEast != null) {
            lblNorth.draw(this);
        }
        if (lblEast != null) {
            lblSouth.draw(this);
        }
        if (lblEast != null) {
            lblWest.draw(this);
        }
    }

    public void mousePressed() {
        if (previousPoint == null) {
            if (xTrans < mouseX && mouseX < xTrans + cityMap.getWidth()
                    && yTrans < mouseY && mouseY < yTrans + cityMap.getHeight()) {
                previousPoint = new Point3d(mouseX, mouseY, 0);
                lblMapLoading.setValue(MAP_LOADING);
            }
            return;
        }
    }

    private Point3d previousPoint = null;
    
    public void mouseDragged() {
        
    }

    public void mouseReleased() {
        try {
            if (previousPoint == null) {
                return;
            }
            if (mouseX > previousPoint.getX()) {
                cityMap.moveLeft(mouseX - previousPoint.getX());
            } else if (mouseX < previousPoint.getX()) {
                cityMap.moveRight(previousPoint.getX() - mouseX);
            }
            
            if (mouseY > previousPoint.getY()) {
                cityMap.moveUp(mouseY - previousPoint.getY());
            } else if (mouseY < previousPoint.getY()) {
                cityMap.moveDown(previousPoint.getY() - mouseY);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        lblMapLoading.setValue(EMPTY);
        previousPoint = null;
    }
    
    private void setLock(Controller theController, boolean theValue) {
        if (theController == null) {
            return;
        }
        theController.setLock(theValue);
        if (theValue) {
            theController.setColorBackground(color(
                    100, 100));
        } else {
            theController.setColorBackground(color(color1));
        }
    }

    public void keyPressed() {
        if (key == '0') {
        }
    }

    public void eraser(float x, float y, float width, float height) {
        fill(backgroundColor);
        rect(x, y, width, height);
    }
    
    public void controlEvent(ControlEvent theEvent) {
        // ListBox is if type ControlGroup.
        // 1 controlEvent will be executed, where the event
        // originates from a ControlGroup. therefore
        // you need to check the Event with
        // if (theEvent.isGroup())
        // to avoid an error message from controlP5.

        if (theEvent.isGroup()) {
            // an event from a group e.g. scrollList
            println(theEvent.group().value() + " from " + theEvent.group());
        }

        if (theEvent.isGroup() && theEvent.name().equals(
                "lbxMapSize")) {
            int value = (int) theEvent.group().value();
            lbxMapSize.setCaptionLabel(tokens[value]);
            lbxMapSize.close();
            cityMap.setSize(value, value);
            setSize(value + 300, value + 100);
            lblStatus.setPosition(value + 300 - 160, 40);
            lblEast.setPosition(value + 300 - 80, 185);
            lblNorth.setPosition(value + 300 - 130, 150);
            lblSouth.setPosition(value + 300 - 130, 220);
            lblWest.setPosition(value + 300 - 175, 185);
            lblMapLoading.setPosition(value + 300 - 175, 500);
            lblImageSaving.setPosition(value + 300 - 175, 300);
            loadCityMap();
        } else if (theEvent.name().equals("play")) {
            setLock(btnPlay, Boolean.TRUE);
            setLock(btnPause, Boolean.FALSE);
            if (timer != null) {
                timer.setSpeedOfTime(sldTimeSpeed.getValue());
            }
            if (lblStatus != null) {
                lblStatus.setValue(EMPTY);
            }
        } else if (theEvent.name().equals("pause")) {
            setLock(btnPause, Boolean.TRUE);
            setLock(btnPlay, Boolean.FALSE);
            if (timer != null) {
                timer.setSpeedOfTime(0);
            }
            if (lblStatus != null) {
                lblStatus.setValue(PAUSED);
            }
        } else if (theEvent.name().equals("reset timer")) {
            if (timer != null) {
                timer.reset();
            }
        } else if (theEvent.name().equals("load west area")) {
            setLock(btnLoadWestArea, Boolean.TRUE);
            setLock(btnLoadEastArea, Boolean.FALSE);
            if (cityMap != null) {
                cityMap.setCsvPath(PATH_DATA + "/" + WEST_AREA_DATA_FILE);
                cityMap.setCenterPoint(new Point3d());
                lblMapLoading.setValue(MAP_LOADING);
                loadCityMap();
                lblMapLoading.setValue(EMPTY);
            }
        } else if (theEvent.name().equals("load east area")) {
            setLock(btnLoadEastArea, Boolean.TRUE);
            setLock(btnLoadWestArea, Boolean.FALSE);
            if (cityMap != null) {
                cityMap.setCsvPath(PATH_DATA + "/" + EAST_AREA_DATA_FILE);
                cityMap.setCenterPoint(new Point3d());
                lblMapLoading.setValue(MAP_LOADING);
                loadCityMap();
                lblMapLoading.setValue(EMPTY);
            }
        } else if (theEvent.name().equals("Time Speed")) {
            if (timer != null && sldTimeSpeed != null) {
                timer.setSpeedOfTime(sldTimeSpeed.getValue());
            }
        } else if (theEvent.name().equals("capture screen")) {
            if (lblImageSaving != null) {
                lblImageSaving.setValue(IMAGE_SAVING);
            }
            draw();
            saveFrame(PATH_DATA + "/take_shot.jpeg");
            if (lblImageSaving != null) {
                lblImageSaving.setValue(EMPTY);
            }
        } else if (theEvent.name().equals("capture with 6 situations")) {
            if (cityMap == null) {
                return;
            }
            
            if (lblImageSaving != null) {
                lblImageSaving.setValue(IMAGE_SAVING);
            }
            float temp = cityMap.getWaterHeight();
            
            int situationNumber = 6; 
            float heightIncreasePerSituation = (cityMap.highestZ() - cityMap.lowestZ()) / situationNumber;
            for (int number = 0; number < situationNumber; number++) {
                cityMap.setWaterHeight(cityMap.lowestZ() + heightIncreasePerSituation * number);
                cityMap.display(this, xTrans, yTrans);
                saveFrame(PATH_DATA + "/situation_" + number + ".jpeg");
            }
            cityMap.setWaterHeight(temp);
            if (lblImageSaving != null) {
                lblImageSaving.setValue(EMPTY);
            }
            
            needDraw = Boolean.TRUE;
        }
    }

    private void loadCityMap() {
        try {
            cityMap.loadCsvData();
            needDraw = Boolean.TRUE;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

    
    






